import {
  _decorator,
  Component,
  Node,
  Prefab,
  instantiate,
  math,
  sp,
  random,
  randomRangeInt,
  Vec3,
  Collider,
  macro,
  Label,
  Animation,
} from 'cc'
import { Bullet } from '../bullet/Bullet'
import { BulletProp } from '../bullet/BulletProp'
import { EnemyPlane } from '../plane/EnemyPlane'
import { SelfPlane } from '../plane/SelfPlane'
import { AudioManager } from './AudioManager'
import { Constant } from './Constant'
import { PoolManager } from './PoolManager'
const { ccclass, property, menu } = _decorator

/**
 * Predefined variables
 * Name = GameManager
 * DateTime = Fri Feb 11 2022 18:27:18 GMT+0800 (中国标准时间)
 * Author = ashscc
 * FileBasename = GameManager.ts
 * FileBasenameNoExtension = GameManager
 * URL = db://assets/script/framework/GameManager.ts
 * ManualUrl = https://docs.cocos.com/creator/3.4/manual/en/
 *
 */

@ccclass('GameManager')
@menu('framework/GameManager')
export class GameManager extends Component {
  //player plane
  @property(SelfPlane)
  public playerPlane:  SelfPlane= null
  //bullet
  @property(Prefab)
  public bullet01: Prefab = null
  @property(Prefab)
  public bullet02: Prefab = null
  @property(Prefab)
  public bullet03: Prefab = null
  @property(Prefab)
  public bullet04: Prefab = null
  @property(Prefab)
  public bullet05: Prefab = null
  @property(Node)
  public bulletRoot: Node = null
  @property
  public bulletSpeed = 1
  @property
  public shootingPeriod = 0.3
  //enemy plane
  @property(Prefab)
  public enemyPlane01: Prefab = null
  @property(Prefab)
  public enemyPlane02: Prefab = null
  @property
  public enemyCreatePeriod = 1
  @property
  public enemyPlane01Speed = 0.5
  @property
  public enemyPlane02Speed = 0.7
  @property(Prefab)
  public enemyExplode: Prefab = null
  //bullet prop
  @property(Prefab)
  public bulletH: Prefab = null
  @property(Prefab)
  public bulletS: Prefab = null
  @property(Prefab)
  public bulletM: Prefab = null
  @property
  public bulletPropSpeed = 0.3
  private _bulletPropType = Constant.BulletPropType.BULLET_M
  //ui Main
  public isGameStart: boolean = false
  //game status
  @property(Node)
  public gamePage: Node = null //游戏界面
  @property(Node)
  public gameoverPage: Node = null //游戏结束界面
  @property(Label)
  public gameScore: Label = null //游戏中分数
  @property(Label)
  public gameoverScore: Label = null //游戏结束分数
  @property(Animation)
  public anim: Animation = null //游戏结束场景动画效果
  //audio音效
  @property(AudioManager)
  public audioManager: AudioManager = null

  //player
  private _isShooting = false
  private _currentShootingTime = 0
  private _score = 0
  //enemy
  private _currentCreateEnemyTime = 0
  private _combinationInterval = Constant.CombinationType.PLANE1

  start() {
    this._init()
  }

  private _init() {
    this._currentShootingTime = this.shootingPeriod
    this._currentCreateEnemyTime = this.enemyCreatePeriod
  }

  public gameStart() {
    this.isGameStart = true
    this.playerPlane.init()
    this.schedule(this._combinationModeChange, 10, macro.REPEAT_FOREVER)
  }

  public gameRestart() {
    this.isGameStart = true
    this.playerPlane.init()
    this._currentShootingTime = 0
    this._score = 0
    this.gameScore.string = this._score.toString() 
    this._currentCreateEnemyTime = 0
    this._combinationInterval = Constant.CombinationType.PLANE1
    this._bulletPropType = Constant.BulletPropType.BULLET_M
    this.playerPlane.node.setPosition(0, 0, 15)
    this.schedule(this._combinationModeChange, 10, macro.REPEAT_FOREVER)
  }

  public returnMain() {
    this._currentShootingTime = 0
    this._score = 0
    this.gameScore.string = this._score.toString() 
    this._currentCreateEnemyTime = 0
    this._combinationInterval = Constant.CombinationType.PLANE1
    this._bulletPropType = Constant.BulletPropType.BULLET_M
    this.playerPlane.node.setPosition(0, 0, 15)
  }

  public gameOver() {
    this.isGameStart = false
    this.gamePage.active = false
    this.gameoverPage.active = true
    this.anim.play()
    this.gameoverScore.string = this._score.toString()
    this._isShooting = false
    this.unschedule(this._combinationModeChange)
    this._destroyAll()
  }

  private _destroyAll() {
    //销毁所有的场景对象
    //gameManager节点下的
    let children = this.node.children
    let length = children.length
    for (let i = length - 1; i >= 0; i--) {
      // children[i].destroy()
      PoolManager.putNode(children[i])
    }

    //bulletRoot下的
    children = this.bulletRoot.children
    length = children.length
    for (let i = length - 1; i >= 0; i--) {
      // children[i].destroy()
      PoolManager.putNode(children[i])
    }
  }

  public addScore() {
    this._score++
    this.gameScore.string = this._score.toString()
  }

  public play(name: string){
    this.audioManager.play(name)
  }

  public createEnemyExplodeEffect(pos: Vec3){
    const enemyExplodeNode = PoolManager.getNode(this.enemyExplode, this.node)
    enemyExplodeNode.setPosition(pos)
  }

  update(deltaTime: number) {
    if(!this.isGameStart){
      return
    }
    if(this.playerPlane.isPlayerDie){
      this.gameOver()
    }
    this._currentShootingTime += deltaTime
    if (this._isShooting && this._currentShootingTime > this.shootingPeriod) {
      if (this._bulletPropType === Constant.BulletPropType.BULLET_H) {
        this._createBulletH()
      } else if (this._bulletPropType === Constant.BulletPropType.BULLET_S) {
        this._createBulletS()
      } else {
        this._createBulletM()
      }

      const name = 'bullet' + (this._bulletPropType % 2 + 1)
      this.play(name)
      this._currentShootingTime = 0
    }

    this._currentCreateEnemyTime += deltaTime
    //判断敌机出现的组合方式去创建敌机
    if (this._combinationInterval === Constant.CombinationType.PLANE1) {
      if (this._currentCreateEnemyTime > this.enemyCreatePeriod) {
        this._createEnemyPlan01()
        this._currentCreateEnemyTime = 0
      }
    } else if (this._combinationInterval === Constant.CombinationType.PLANE2) {
      const combinationType = math.randomRangeInt(1, 3)
      if (this._currentCreateEnemyTime > this.enemyCreatePeriod * 0.9) {
        if (combinationType === Constant.CombinationType.PLANE2) {
          this._createEnemyPlane02()
        } else {
          this._createEnemyPlan01()
        }
        this._currentCreateEnemyTime = 0
      }
    } else {
      const combinationType = math.randomRangeInt(1, 4)
      if (this._currentCreateEnemyTime > this.enemyCreatePeriod * 0.8) {
        if (combinationType === Constant.CombinationType.PLANE3) {
          this._createEnemyPlane03()
        } else if (combinationType === Constant.CombinationType.PLANE2) {
          this._createEnemyPlane02()
        } else {
          this._createEnemyPlan01()
        }
        this._currentCreateEnemyTime = 0
      }
    }
  }

  private _createEnemyPlan01() {
    const whichEnemy = math.randomRangeInt(1, 3)
    let prefab: Prefab = null
    let speed = 0
    if (whichEnemy === Constant.EnemyPlaneType.TYPE1) {
      prefab = this.enemyPlane01
      speed = this.enemyPlane01Speed
    } else {
      prefab = this.enemyPlane02
      speed = this.enemyPlane02Speed
    }
    // const enemyPlane = instantiate(prefab)
    // enemyPlane.setParent(this.node)
    const enemyPlane = PoolManager.getNode(prefab, this.node)
    const enemyComp = enemyPlane.getComponent(EnemyPlane)
    enemyComp.show(this, speed, true)

    enemyPlane.setPosition(math.randomRangeInt(-25, 26), 0, -50)
  }

  private _createEnemyPlane02() {
    const enemyArray = new Array<Node>(5)
    const enemyType = randomRangeInt(1, 3)
    let speed = 0
    for (let i = 0; i < enemyArray.length; i++) {
      if (enemyType === Constant.EnemyPlaneType.TYPE1) {
        // enemyArray[i] = instantiate(this.enemyPlane01)
        enemyArray[i] = PoolManager.getNode(this.enemyPlane01, this.node)
        speed = this.enemyPlane01Speed
      } else {
        // enemyArray[i] = instantiate(this.enemyPlane02)
        enemyArray[i] = PoolManager.getNode(this.enemyPlane02, this.node)
        speed = this.enemyPlane02Speed
      }
      const element = enemyArray[i]
      const enemyComp = element.getComponent(EnemyPlane)
      enemyComp.show(this, speed, false)
      // element.setParent(this.node)
      element.setPosition(-20 + i * 10, 0, -50)
    }
  }

  private _createEnemyPlane03() {
    const enemyArray = new Array<Node>(7)
    const enemyType = randomRangeInt(1, 3)
    let speed = 0

    const enemyPlanesPos = [
      -21, 0, -60, -14, 0, -55, -7, 0, -50, 0, 0, -45, 7, 0, -50, 14, 0, -55,
      21, 0, -60,
    ]
    for (let i = 0; i < enemyArray.length; i++) {
      if (enemyType === Constant.EnemyPlaneType.TYPE1) {
        // enemyArray[i] = instantiate(this.enemyPlane01)
        enemyArray[i] = PoolManager.getNode(this.enemyPlane01, this.node)
        speed = this.enemyPlane01Speed
      } else {
        // enemyArray[i] = instantiate(this.enemyPlane02)
        enemyArray[i] = PoolManager.getNode(this.enemyPlane02, this.node)
        speed = this.enemyPlane02Speed
      }
      const element = enemyArray[i]
      const enemyComp = element.getComponent(EnemyPlane)
      const startIndex = i * 3
      enemyComp.show(this, speed, false)
      // element.setParent(this.node)
      element.setPosition(
        enemyPlanesPos[startIndex],
        enemyPlanesPos[startIndex + 1],
        enemyPlanesPos[startIndex + 2]
      )
    }
  }

  public createEnemyBullet(enemyPlanePos: Vec3, name: string) {
    // const bullet = instantiate(this.bullet01)
    // bullet.setParent(this.bulletRoot)
  
    let bullet: Node = null
    if(name === 'plane02'){
        bullet = PoolManager.getNode(this.bullet02, this.bulletRoot)
    }else {
        bullet = PoolManager.getNode(this.bullet04, this.bulletRoot)
    }
    bullet.setPosition(enemyPlanePos.x, enemyPlanePos.y, enemyPlanePos.z + 2)
    const bulletComp = bullet.getComponent(Bullet)
    bulletComp.show(this.bulletSpeed, true)
    const colliderComp = bullet.getComponent(Collider)
    colliderComp.setGroup(Constant.CollisionType.ENEMY_BULLET)
    colliderComp.setMask(Constant.CollisionType.SELF_PLAYER)
  }

  public changePlayerBullet(bulletPropType: number) {
    this._bulletPropType = bulletPropType
  }

  private _createBulletM() {
    // const bullet = instantiate(this.bullet01)
    // bullet.setParent(this.bulletRoot)
    const bullet = PoolManager.getNode(this.bullet01, this.bulletRoot)
    const pos = this.playerPlane.node.position
    bullet.setPosition(pos.x, pos.y, pos.z - 2)
    const bulletComp = bullet.getComponent(Bullet)
    bulletComp.show(this.bulletSpeed, false)
    const colliderComp = bullet.getComponent(Collider)
    colliderComp.setGroup(Constant.CollisionType.SELF_BULLET)
    colliderComp.setMask(Constant.CollisionType.ENEMY_PLAYER)
  }

  private _createBulletH() {
    const pos = this.playerPlane.node.position
    //left
    // const bullet = instantiate(this.bullet03)
    // bullet.setParent(this.bulletRoot)
    const bullet = PoolManager.getNode(this.bullet03, this.bulletRoot)
    bullet.setPosition(pos.x - 2.5, pos.y, pos.z - 2)
    const bulletComp = bullet.getComponent(Bullet)
    bulletComp.show(this.bulletSpeed, false)
    const colliderComp = bullet.getComponent(Collider)
    colliderComp.setGroup(Constant.CollisionType.SELF_BULLET)
    colliderComp.setMask(Constant.CollisionType.ENEMY_PLAYER)

    //right
    // const bullet1 = instantiate(this.bullet03)
    // bullet1.setParent(this.bulletRoot)
    const bullet1 = PoolManager.getNode(this.bullet03, this.bulletRoot)
    bullet1.setPosition(pos.x + 2.5, pos.y, pos.z - 2)
    const bulletComp1 = bullet1.getComponent(Bullet)
    bulletComp1.show(this.bulletSpeed, false)
    const colliderComp1 = bullet1.getComponent(Collider)
    colliderComp1.setGroup(Constant.CollisionType.SELF_BULLET)
    colliderComp1.setMask(Constant.CollisionType.ENEMY_PLAYER)
  }

  private _createBulletS() {
    const pos = this.playerPlane.node.position
    //left
    // const bullet = instantiate(this.bullet05)
    // bullet.setParent(this.bulletRoot)
    const bullet = PoolManager.getNode(this.bullet05, this.bulletRoot)
    bullet.setPosition(pos.x - 4, pos.y, pos.z - 2)
    const bulletComp = bullet.getComponent(Bullet)
    bulletComp.show(this.bulletSpeed, false, Constant.BulletType.LEFT)
    const colliderComp = bullet.getComponent(Collider)
    colliderComp.setGroup(Constant.CollisionType.SELF_BULLET)
    colliderComp.setMask(Constant.CollisionType.ENEMY_PLAYER)

    //middle
    // const bullet1 = instantiate(this.bullet05)
    // bullet1.setParent(this.bulletRoot)
    const bullet1 = PoolManager.getNode(this.bullet05, this.bulletRoot)
    bullet1.setPosition(pos.x, pos.y, pos.z - 2)
    const bulletComp1 = bullet1.getComponent(Bullet)
    bulletComp1.show(this.bulletSpeed, false)
    const colliderComp1 = bullet1.getComponent(Collider)
    colliderComp1.setGroup(Constant.CollisionType.SELF_BULLET)
    colliderComp1.setMask(Constant.CollisionType.ENEMY_PLAYER)

    //right
    // const bullet2 = instantiate(this.bullet05)
    // bullet2.setParent(this.bulletRoot)
    const bullet2 = PoolManager.getNode(this.bullet05, this.bulletRoot)
    bullet2.setPosition(pos.x + 4, pos.y, pos.z - 2)
    const bulletComp2 = bullet2.getComponent(Bullet)
    bulletComp2.show(this.bulletSpeed, false, Constant.BulletType.RIGHT)
    const colliderComp2 = bullet2.getComponent(Collider)
    colliderComp2.setGroup(Constant.CollisionType.SELF_BULLET)
    colliderComp2.setMask(Constant.CollisionType.ENEMY_PLAYER)
  }

  private _createBulletProp() {
    const bulletPropType = math.randomRangeInt(1, 4)
    let propPrefab: Prefab = null
    if (bulletPropType === Constant.BulletPropType.BULLET_H) {
      propPrefab = this.bulletH
    } else if (bulletPropType === Constant.BulletPropType.BULLET_S) {
      propPrefab = this.bulletS
    } else {
      propPrefab = this.bulletM
    }

    // let bulletProp = instantiate(propPrefab)
    // bulletProp.setParent(this.bulletRoot)
    let bulletProp = PoolManager.getNode(propPrefab, this.bulletRoot)
    bulletProp.setPosition(15, 0, -50)
    const bulletComp = bulletProp.getComponent(BulletProp)
    bulletComp.show(this, -this.bulletPropSpeed)
  }

  public isShooting(_isShooting: boolean) {
    this._isShooting = _isShooting
  }

  private _combinationModeChange() {
    this._combinationInterval++
    this._createBulletProp()
  }
}

/**
 * [1] Class member could be defined like this.
 * [2] Use `property` decorator if your want the member to be serializable.
 * [3] Your initialization goes here.
 * [4] Your update function goes here.
 *
 * Learn more about scripting: https://docs.cocos.com/creator/3.4/manual/en/scripting/
 * Learn more about CCClass: https://docs.cocos.com/creator/3.4/manual/en/scripting/decorator.html
 * Learn more about life-cycle callbacks: https://docs.cocos.com/creator/3.4/manual/en/scripting/life-cycle-callbacks.html
 */
